/* Copyright © 2010 LPRM
Universidade Federal do Espírito Santo - RNP-GT CWTools

This file is part of GW-Android.

GW-Android is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

GW-Android is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.

You should have received a copy of the GNU Lesser General Public License
along with GW-Android. If not, see <http://www.gnu.org/licenses/>. */
package br.ufes.cwtools.gw.android.components.map;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.util.List;
import java.util.Timer;
import java.util.TimerTask;

import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
import android.graphics.drawable.Drawable;
import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
import android.os.Message;
import android.os.RemoteException;
import android.util.Log;
import android.view.KeyEvent;
import android.view.Menu;
import android.view.MenuItem;
import android.widget.Toast;
import br.ufes.cwtools.gw.android.components.Component;
import br.ufes.cwtools.gw.android.connection_service.ConnectionServiceConnection;
import br.ufes.cwtools.gw.android.connection_service.IConnectionService;
import br.ufes.cwtools.gw.android.connection_service.IConnectionServiceCallback;
import br.ufes.cwtools.gw.android.location_service.GeoObject;
import br.ufes.cwtools.gw.android.location_service.GeoObjectsParser;
import br.ufes.cwtools.gw.android.location_service.ILocationService;

import com.google.android.maps.MapActivity;
import com.google.android.maps.MapView;

public class Map extends MapActivity {
	/* Como esta classe não herda Component é preciso tomar cuidado especial
	 * para lidar com o ciclo de vida dos componentes (incluindo incluir no
	 * result o intent recebido, para ser passado adiante). */
	private static final String LOG_TAG = "Map (Component)";
	
//	private static final String CONFIGURATION;
	
	private String GOOGLE_MAPS_KEY;
	
	private static final int MENU_VIEW_MAP = 1;
	private static final int MENU_VIEW_SAT = 2;
	
	public static final String EXTRA_GEO_OBJECTS_URL = "br.ufes.android.components.extra_geo_objects_url";
	public static final String URLS_SEPARATOR = " ";
	private String[] sUrls = null;
	
	private static final long PERIOD = 600000;

	private List<GeoObject> geoObjects;
	private MapView mapView;
	
	private Timer geoObjectsUpdateTimer;
	
	private Context context = this;
	private boolean problemFlag = false;
	
	private InputStream source = null;

	@Override
	protected void onCreate(Bundle icicle) {
		super.onCreate(icicle);
		
		GOOGLE_MAPS_KEY = "03ukYISgJA1Ff7E9UdnxHB2_m4NCA7TbGO_C2Fw";
		
		mapView = new MapView(this, GOOGLE_MAPS_KEY);
		
		mapView.setClickable(true);
		mapView.setBuiltInZoomControls(true);
		this.setContentView(mapView);
	
		getGeoObjectsUrls();
		
		geoObjectsUpdateTimer = new Timer();
		geoObjectsUpdateTimer.scheduleAtFixedRate(new PopulateGeoObjectsPolling(), 0, PERIOD);
		Log.d(LOG_TAG, ""+this.getIntent().getStringExtra("sample_setting"));  // TODO Exemplo do uso de settings.
	}

	@Override
	public void finish() {
		setResult(RESULT_OK, getIntent());
		super.finish();
	}
	
	@Override
	public boolean onKeyDown(int keyCode, KeyEvent event) {
		if (keyCode == KeyEvent.KEYCODE_BACK) {
			setResult(Component.RESULT_BACK, getIntent());
			super.finish();
			return true;
		}
		return super.onKeyDown(keyCode, event);
	}
	
	public void drawMap(List<GeoObject> geoObjects){
		
		Drawable drawable1 = this.getResources().getDrawable(R.drawable.camera_icon);
		Drawable drawable2 = this.getResources().getDrawable(R.drawable.androidmarker);
		GeoObjectItemizedOverlay itemizedOverlay1 = new GeoObjectItemizedOverlay(drawable1, this);
		GeoObjectItemizedOverlay itemizedOverlay2 = new GeoObjectItemizedOverlay(drawable2, this);
		for (GeoObject g : geoObjects) {
			if (g.getType().equalsIgnoreCase(GeoObject.TYPE_PHOTO)) {
				mapView.getOverlays().add(itemizedOverlay1);
				itemizedOverlay1.addGeoObject(g);
			} else if (g.getType().equalsIgnoreCase(GeoObject.TYPE_USER)) {
				mapView.getOverlays().add(itemizedOverlay2);
				itemizedOverlay2.addGeoObject(g);
			} else {
				// Invalid type?
			}
		}
	}
	
	void getGeoObjectsUrls() {
		String sExtra = this.getIntent().getStringExtra(EXTRA_GEO_OBJECTS_URL);
		//Caso base, se nao for passada nenhuma url
		if (sExtra == null) {
			source = getResources().openRawResource(R.raw.sample_geo_objects); 
//			bindService(new Intent(ILocationService.class.getName()), mLocation, Context.BIND_AUTO_CREATE);
			return;
		}
		if (sExtra.contains("<GeoObjects>")){
			source = new ByteArrayInputStream(sExtra.getBytes());
		}else
			sUrls = sExtra.split(URLS_SEPARATOR);
	}

	private void populateGeoObjects() {
		// TODO Tratar mais de um 
		if (problemFlag){
			return;
		}
		if (sUrls != null){
			requestInputStream();
		}else{
			parseInputStream(source);
		}
	}
	
	private void requestInputStream() {
		bindService(new Intent(IConnectionService.class.getName()), mConnection, Context.BIND_AUTO_CREATE);
	}
	
	private final ServiceConnection mConnection = new ServiceConnection() {
		IConnectionServiceCallback mCallback = new IConnectionServiceCallback.Stub() {
			@Override
			public void requestFinished(String id, String filePath) throws RemoteException {
				Log.d(LOG_TAG, "Callback de download recebido.");
				// Chama handler para acessar a view.
				mMapHandler.sendMessage(mMapHandler.obtainMessage(MAP_MSG, filePath));
			}
		};
		
		@Override
		public void onServiceConnected(ComponentName name, IBinder service) {
			IConnectionService connectionService = IConnectionService.Stub.asInterface(service);
			for (String url : sUrls) {
				// Faz a requisição de download para cada imagem.
				try {
					connectionService.registerGetRequest(url,
							ConnectionServiceConnection.generateId(),
							Map.class.getName(), true, mCallback);
				} catch (RemoteException e) { }
			}
		}
		
		@Override
		public void onServiceDisconnected(ComponentName name) { }
	};
	
	
	private final ServiceConnection mLocation = new ServiceConnection(){
		@Override
		public void onServiceConnected(ComponentName name, IBinder service) {
			ILocationService locationService = ILocationService.Stub.asInterface(service);
			try {
				double lat = locationService.getLatitude();
				double lon = locationService.getLongitude();
				sUrls = new String[1];
//				sUrls[0] = sUrl + String.valueOf(lat) + "/" + String.valueOf(lon) + sUrlSufix;
			} catch (RemoteException e) {
				e.printStackTrace();
			}catch (NullPointerException e){
				problemFlag = true;
				Log.e(LOG_TAG, "Localização ainda não foi fixada!");
				Toast.makeText(context, "Localização ainda não foi fixada!", Toast.LENGTH_LONG).show();
			}
			
		}

		@Override
		public void onServiceDisconnected(ComponentName name) {}
	};
	
	private static final int MAP_MSG = 1;
	
	private Handler mMapHandler = new Handler() {
		@Override
		public void handleMessage(Message msg) {
			switch (msg.what) {
			case MAP_MSG:
				String filePath = (String) msg.obj;
				try {
					source = new FileInputStream(filePath);
					//String undef = inputStreamToString(source);
					//if(undef.contains("<GeoObjects>"))
						parseInputStream(source);
					//else{
						//Caso seja passado uma url para fazer o download de varias urls, Adicionar novos downloads aqui!
						//sUrls = undef.split(URLS_SEPARATOR);
					//}
				} catch (FileNotFoundException e) {
					e.printStackTrace();
				}
				break;
			default:
				super.handleMessage(msg);
			}
		}
	};
	
	public void parseInputStream(InputStream source){
		geoObjects = GeoObjectsParser.parse(source);  // Obtém lista de GeoObjects a serem mostrados no mapa.
		if (geoObjects == null) {
			Log.d(LOG_TAG, "Parser returned null GeoObject list.");
			return;
		}
		drawMap(geoObjects);
	}
	
	public String inputStreamToString(InputStream is){
		ByteArrayOutputStream bout = new ByteArrayOutputStream();
		byte [] buffer = new byte[1024];
		while(true) {
			int len = 0;
			try {
				len = is.read(buffer);
			} catch (IOException e) {
				e.printStackTrace();
			}
			if(len < 0) {
				break;
			}
			bout.write(buffer, 0, len);
		}
		final byte [] data = bout.toByteArray();
		return new String (data);
	}
	
	
	

	@Override
	public boolean onCreateOptionsMenu(Menu menu) {
		menu.add(0, MENU_VIEW_MAP, 0, "Mapa");
		menu.add(0, MENU_VIEW_SAT, 0, "Satélite");
		return true;
	}

	@Override
	public boolean onOptionsItemSelected(MenuItem item) {
		switch (item.getItemId()) {
		case MENU_VIEW_MAP:
			mapView.setSatellite(false);
			return true;
		case MENU_VIEW_SAT:
			mapView.setSatellite(true);
			return true;
		default:
			return false;
		}
	}
	@Override
	public void onDestroy(){
		super.onDestroy();
//		unbindService(mConnection);
	}

	@Override
	protected boolean isRouteDisplayed() {
		return false;
	}
	
	private class PopulateGeoObjectsPolling extends TimerTask{
		
		public void run(){
			populateGeoObjects();
		}
	}
}